package com.swkl.yuan.agvtask.service;

import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
import com.swkl.yuan.agvtask.entity.AllConfig;
import com.swkl.yuan.agvtask.entity.QueryTaskStatusResponseEntity;
import com.swkl.yuan.agvtask.task.SendToPlc;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;


/**
 * @Author Alkin
 * @Date 2023/3/7 12:34
 * @Version 1.0
 */
@Service
@Slf4j
public class FinishTraversalTask {


    private final ConcurrentMap<String, AtomicBoolean> taskLockMap = new ConcurrentHashMap<>();

    /**
     * 完成遍历请求
     * @param taskCodes 需要查询的任务编号
     * @param finishOffset 完成后的偏移量
     * @param flag 是否为第一次。如果为第一次延迟60秒执行 等待
     */
    @SneakyThrows
    @Async
    public void finishTraversal2(String taskCodes,String finishOffset,boolean flag,String requestFinishApi, S7PLC connector){
        if (flag){
            log.info("遍历查询任务状态 id = {}",taskCodes);
            Thread.sleep(AllConfig.finishWaitTime);
        }
        String result = HttpRequest.post(requestFinishApi)
                .body(getRequestParamMap(taskCodes))
                .execute().body();
        JSONObject jsonObject = JSONUtil.toBean(result, JSONObject.class);
        JSONArray jsonArray = (JSONArray)jsonObject.get("data");
        QueryTaskStatusResponseEntity responseEntity = JSONUtil.toBean(jsonArray.get(0).toString(), QueryTaskStatusResponseEntity.class);
        if ("9".equals(responseEntity.getTaskStatus())){
            log.info("id = {}任务完成，反馈PLC",taskCodes);
            SendToPlc.sendFinishSig(connector,finishOffset);
        }else {
            Thread.sleep(1000*3);
            finishTraversal2(taskCodes,finishOffset,false,requestFinishApi,connector);
        }
    }

    @Async
    public void finishTraversal(String taskCodes, String finishOffset, boolean flag,
                                String requestFinishApi, S7PLC connector) {


        // 1. 获取或创建原子锁
        AtomicBoolean lock = taskLockMap.computeIfAbsent(taskCodes, k -> new AtomicBoolean(false));

        // 2. 尝试加锁（失败则直接返回）
        if (!lock.compareAndSet(false, true)) {
            log.warn("任务正在执行中 | taskCodes={}", taskCodes);
            return;
        }
        int retryCount = 0;
        final int maxRetry = 80; // 从配置获取，例如 100
        final long timeoutMs = 15*60*1000; // 例如 30*60*1000 (30分钟)
        final long startTime = System.currentTimeMillis();
        String result = "";
        try {
            // 初始延迟（仅在第一次flag=true时生效）
            if (flag) {
                log.info("启动任务状态轮询 | taskCodes={}", taskCodes);
                sleepWithInterruptCheck(AllConfig.finishWaitTime);
            }

            // 循环核心逻辑
            while (retryCount < maxRetry && !isTimeout(startTime, timeoutMs)) {
                retryCount++;
                log.debug("开始第{}次状态查询 | taskCodes={}", retryCount, taskCodes);

                try {
                    // 1. 发送HTTP请求
                     result = HttpRequest.post(requestFinishApi)
                            .body(getRequestParamMap(taskCodes))
                            .timeout(10*1000) // 设置HTTP超时
                            .execute()
                            .body();

                    // 2. 解析JSON响应
                    JSONObject jsonObject = JSONUtil.parseObj(result);
                    if (!jsonObject.containsKey("data")) {
                        log.error("响应数据格式异常 | response={}", jsonObject);
                        break;
                    }

                    JSONArray jsonArray = jsonObject.getJSONArray("data");
                    if (jsonArray.isEmpty()) {
                        log.error("data字段为空数组 | response={}", jsonObject);
                        break;
                    }

                    QueryTaskStatusResponseEntity response = JSONUtil.toBean(
                            jsonArray.getStr(0), QueryTaskStatusResponseEntity.class);

                    // 3. 状态判断
                    if ("9".equals(response.getTaskStatus())) {
                        log.info("任务完成通知PLC | taskCodes={}", taskCodes);
                        SendToPlc.sendFinishSig(connector, finishOffset);
                        return; // 成功退出
                    } else {
                        log.debug("任务未完成等待重试 | status={}", response.getTaskStatus());
                    }

                } catch (HttpException e) {
                    log.error("HTTP请求失败 | url={} error={}", requestFinishApi, e.getMessage());
                } catch (JSONException e) {
                    log.error("JSON解析失败 | response={}", result, e);
                }

                // 4. 间隔等待（含中断检查）
                sleepWithInterruptCheck(3*1000);
            }

            // 循环退出后的错误处理
            if (retryCount >= maxRetry) {
                log.error("超过最大重试次数 | maxRetry={} taskCodes={}", maxRetry, taskCodes);
            } else if (isTimeout(startTime, timeoutMs)) {
                log.error("任务执行超时 | timeout={}ms taskCodes={}", timeoutMs, taskCodes);
            }
        } catch (Exception e) {
            log.error("任务轮询异常终止 | taskCodes={}", taskCodes, e);
        } finally {
            // 4. 释放锁（确保异常时也能释放）
            lock.set(false);
            // 可选：根据业务决定是否保留
            taskLockMap.remove(taskCodes);
        }
    }

    // 工具方法：带中断检查的睡眠
    private void sleepWithInterruptCheck(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            log.warn("线程被中断终止轮询");
            Thread.currentThread().interrupt(); // 恢复中断状态
            throw new RuntimeException("任务轮询被中断", e);
        }
    }

    // 工具方法：超时判断
    private boolean isTimeout(long startTime, long timeoutMs) {
        return System.currentTimeMillis() - startTime > timeoutMs;
    }


    private String getRequestParamMap(String taskCodes){
        JSONObject json = new JSONObject();
        json.set("reqCode", IdUtil.simpleUUID());
        json.set("taskCodes",new String[]{taskCodes});
        return JSONUtil.toJsonStr(json);
    }
}
